Skip to main content

OV5647 Camera Module Adaptation Guide

This guide uses the OV5647 camera module as an example to detail the development and adaptation process on the NG4520 platform, covering hardware preparation, device tree configuration, driver compilation, system loading, and debugging steps.

Terms and Explanations

TermsExplanations
ISPConverts raw sensor data into usable images, handling demosaicing, noise reduction, exposure, focus, and white balance.
BayerBayer sensors uses alternating red, green, and blue filters over photosites, with green taking half, to capture color images.
CSIHigh-speed serial interface for transferring image data between sensors and processors.
V4L2Linux kernel driver interface for video capture and output devices.
libargusNVIDIA API for acquiring camera images and metadata.
Camera Core LibraryProvides controls and processing between user applications and V4L2 drivers.
GStreamerMultimedia framework supporting playback and advanced audio/video processing.
Host1xTegra DMA engine managing graphics/multimedia register access and sync via client FIFOs.
ApertureControls the size of the lens opening.
UVCUSB video devices with standard streaming functions that connect directly to hosts.
VITransfers video data from an input device.

Software Driver Architecture

The NVIDIA platform supports two types of camera driver architectures:

  • Camera Core Library Interface: Enables access to the ISP and advanced image processing. Applications interact through the libargus API (e.g., the nvgstcapture-1.0 tool).
  • Direct V4L2 Interface:Directly uses the V4L2 driver without the NVIDIA ISP.

It is recommended to use the Camera Core Library Interface to fully leverage the capabilities of the NVIDIA ISP.

OV5647 Driver Configuration Process

Prerequisites

  • To find the I2C address and pinout of a camera module,verify the Power-Down (PWDN) and RESET pins.

  • Obtain the device’s power-up sequence, external clock parameters, supported resolutions and frame rates, and the color filter array type(BGGR/GBRG/GRBG/RGGB).

  • Confirm the connection interface and the corresponding  tegra_sinterface  mapping (for example, on the NG452X platform, cam0 = serial_c).

This example is based on Jetson Orin NX, with cam0 as the hardware connection interface.

To add camera modules to a device tree

  • Locate or create a tegra-camera-platform device tree node in the kernel source tree at:

    Linux_for_Tegra/source/hardware/nvidia/t23x/nv-public/overlay/tegra234-p3767-camera-p3768-ov5647.dts`
  • Configure the  tegra-camera-platform  node, including video stream ports and connection relationships. For example:

    tegra-capture-vi  {
    num-channels = <1>;
    ports {
    #address-cells = <1>;
    #size-cells = <0>;
    vi_port1: port@1 {
    reg = <0>;
    rbpcv2_ov5647_vi_in1: endpoint {
    port-index = <2>;
    bus-width = <2>;
    remote-endpoint = <&rbpcv2_ov5647_csi_out1>;
    };
    };
    };
    };
  • I2C Device Node Configuration

    Configure camera parameters (I2C address, modes, clock, resolution, etc.):

    rbpcv2_ov5647_c@36 {
    reset-gpios = <&gpio CAM0_RST GPIO_ACTIVE_HIGH>;
    pwdn-gpios = <&gpio CAM1_PWDN GPIO_ACTIVE_HIGH>;
    compatible = "ovti,ov5647";
    /* I2C device address */
    reg = <0x36>;
    /* V4L2 device node loation */
    devnode = "video0";
    /* Physical dimensions of sensor */
    physical_w = "3.670";
    physical_h = "2.740";
    sensor_model = "ov5647";
    use_sensor_mode_id = "true";

    clocks = <&bpmp TEGRA234_CLK_EXTPERIPH2>,
    <&bpmp TEGRA234_CLK_PLLP_OUT0>;
    clock-names = "extperiph2", "pllp_grtba";
    mclk = "extperiph2";
    clock-frequency = <24000000>;

    mode0 { /* ov5647_MODE0_1920x1080_30FPS */
    mclk_khz = "25000";
    num_lanes = "2";
    // lane_polarity = "4";
    tegra_sinterface = "serial_c";
    phy_mode = "DPHY";
    discontinuous_clk = "yes";
    dpcm_enable = "false";
    cil_settletime = "0";
    active_w = "1920";
    active_h = "1080";
    mode_type = "bayer";
    pixel_phase = "bggr";
    csi_pixel_bit_depth = "10";
    readout_orientation = "90";
    line_length = "2416";
    inherent_gain = "1";
    mclk_multiplier = "3.6669";
    pix_clk_hz = "81666663";
    gain_factor = "16";
    framerate_factor = "1000000";
    exposure_factor = "1000000";
    min_gain_val = "16";
    max_gain_val = "128";
    step_gain_val = "1";
    default_gain = "16";
    min_hdr_ratio = "1";
    max_hdr_ratio = "1";
    min_framerate = "30000000"; /* 30.0 fps */
    max_framerate = "30000000"; /* 30.0 fps */
    step_framerate = "1";
    default_framerate = "45000000"; /* 30.0 fps */
    min_exp_time = "60"; /* us */
    max_exp_time = "30000"; /* us */
    step_exp_time = "1";
    default_exp_time = "10000"; /* us */
    embedded_metadata_height = "0";
    };

    ........................

    ports {
    #address-cells = <1>;
    #size-cells = <0>;
    port@0 {
    reg = <0>;
    rbpcv2_ov5647_out1: endpoint {
    status = "okay";
    port-index = <1>;
    bus-width = <2>;
    remote-endpoint = <&rbpcv2_ov5647_csi_in1>;
    };
    };
    };

    };
  • Port Binding Configuration

    • Useremote-endpoint to connect the camera, CSI, and VI modules, ensuring a complete video stream pipeline.

      tegra-capture-vi  {
      num-channels = <1>;
      ports {
      #address-cells = <1>;
      #size-cells = <0>;
      vi_port1: port@1 {
      reg = <0>;
      rbpcv2_ov5647_vi_in1: endpoint {
      port-index = <2>;
      bus-width = <2>;
      remote-endpoint = <&rbpcv2_ov5647_csi_out1>;
      };
      };
      };
      };

      host1x@13e00000 {
      nvcsi@15a00000 {
      num-channels = <1>;
      #address-cells = <1>;
      #size-cells = <0>;
      csi_chan1: channel@1 {
      reg = <0>;
      ports {
      #address-cells = <1>;
      #size-cells = <0>;
      csi_chan1_port0: port@0 {
      reg = <0>;
      rbpcv2_ov5647_csi_in1: endpoint@2 {
      port-index = <2>;
      bus-width = <2>;
      remote-endpoint = <&rbpcv2_ov5647_out1>;
      };
      };
      csi_chan1_port1: port@1 {
      reg = <1>;
      rbpcv2_ov5647_csi_out1: endpoint@3 {
      remote-endpoint = <&rbpcv2_ov5647_vi_in1>;
      };
      };
      };
      };
      };
      };
  • Overlay Makefile Configuration,add the following in overlay/Makefile

    dtbo-y += tegra234-p3767-camera-p3768-ov5647.dtbo
  • Build the dtbo:

    make dtbs

Building Driver

  • Add Driver Source Code

    • Example pat:drivers/media/i2c/ov5647.c
    • It is recommended to modify it based on nv_ov5693.c 
    • Main functions include:
      • Usetegracam_device_register to register the driver
      • Set up these key function structures:
        • tc_dev->sensor_ops(power on/off, register read/write, mode setting, stream control, etc.)
        • tc_dev->v4l2sd_internal_ops(e.g., open/s_stream)
        • tc_dev->tcctrl_ops(gain, exposure, frame rate control, etc.)
    static struct tegracam_sensor_ops ov5647_sensor_ops = {
    .power_on = ov5647_power_on,
    .power_off = ov5647_power_off,
    .write_reg = ov5647_write_reg,
    .read_reg = ov5647_read_reg,
    .set_mode = ov5647_set_mode,
    .start_streaming = ov5647_start_streaming,
    .stop_streaming = ov5647_stop_streaming,
    .sensor_init = ov5647_sensor_init,
    };

The implementation of these functions should follow the OV5647 datasheet and related documentation

  • Add Register Table Header File

    • Example path:drivers/media/i2c/ov5647_modes_tbls.h
    • This should include register configuration tables for various resolutions and exposure settings, and keep the same order as defined in the device tree modes.
    static struct v4l2_subdev_internal_ops ov5647_subdev_internal_ops = {
    .open = ov5647_open,
    };
  • Driver Makefile Configuration,add the following line to to drivers/media/i2c/Makefile to include the OV5647 driver:

    obj-m += ov5647.o
  • Compile the kernel module:

    make modules

Driver Installation and Loading

Copy Files

  • Copy the generated .dtbo and ov5647.ko to the target device (for example, NG4520):
cd Linux_for_Tegra/source

scp ./kernel-devicetree/generic-dts/dtbs/tegra234-p3767-camera-p3768-ov5647.dtbo milesight@192.168.60.3:/home/milesight
scp ./kernel-devicetree/generic-dts/dtbs/tegra234-p3767-camera-p3768-ov5647.dtbo milesight@192.168.60.3:/home/milesight

Driver Installation

  • Copyov5647.ko to the modules directory and run depmod to load the driver:
sudo cp ./ov5647.ko  /usr/lib/modules/5.15.148-tegra/updates/drivers/media/i2c/

depmod
  • Copy.dtbo to /boot,and load it using the config-by-hardware.py 

  • After completing these steps, reboot the development board.

Verification Process

  1. Check the Device Tree Load

    • Check ifextlinux.conf has loaded the .dtbo

      cat /boot/extlinux/extlinux.conf

    • Verify whether the device tree node is effective:

      cat /proc/device-tree/tegra-camera-platform/modules/module1/drivernode0/sysfs-device-tree

  2. Check the Driver Load

    • Check the driver module using the following command:

      lsmod | grep ov5647 sudo dmesg | grep ov5647

    • Check I2C device matching using the following command:

      sudo i2cdetect -y -r 9

  3. Camera Function Test

    • Run nvgstcapture-1.0,to verify resolution and mode matching, and ensure there are no errors.

    • Check the video stream pipeline using the following command:

      sudo media-ctl -p -d /dev/media0

References

Sensor Software Driver Programming

ov5647_driver/ov5647.c at main · mkm684/ov5647_driver


For adapting to other camera models, Please refer to the original vendor datasheet and the NVIDIA official documentation, as the steps are generally similar.